﻿using Newtonsoft.Json;
using System.Collections.Concurrent;
using System.Collections.ObjectModel;
using System.IO;
using VideoTools.vo;
using TaskStatus = VideoTools.vo.TaskStatus;

namespace VideoTools.Tasks
{
    public class DownloadM3u8TaskQueueManager : NotifyPropertyBase
    {
        private static volatile DownloadM3u8TaskQueueManager _instance;
        private static readonly object SyncRoot = new object();

        public static DownloadM3u8TaskQueueManager Instance
        {
            get
            {
                if (_instance == null)
                {
                    lock (SyncRoot)
                    {
                        if (_instance == null)
                        {
                            _instance = new DownloadM3u8TaskQueueManager();
                        }
                    }
                }

                return _instance;
            }
        }

        private DownloadM3u8TaskQueueManager() { }

        private readonly ObservableCollection<DownloadM3u8TaskItem> _tasks =
            new ObservableCollection<DownloadM3u8TaskItem>();
        public IReadOnlyCollection<DownloadM3u8TaskItem> Tasks => _tasks;

        private int _maxConcurrentTasks = 3;
        public int MaxConcurrentTasks
        {
            get => _maxConcurrentTasks;
            set
            {
                SetProperty(ref _maxConcurrentTasks, value);
                StartProcessing(); // 当并发数改变时尝试启动新任务
            }
        }
        private IList<DownloadM3u8TaskItem> _SelectedTasks = new List<DownloadM3u8TaskItem>();
        public IList<DownloadM3u8TaskItem> SelectedTasks
        {
            get { return _SelectedTasks; }
            set
            {
                SetProperty(ref _SelectedTasks ,value);
                SetBackgroundColor();
                foreach (var ele in value)
                {
                    ele.BgColour = ListBoxLineBackground.Selected;
                }
            }
        }
        public readonly ConcurrentDictionary<DownloadM3u8TaskItem, bool> _runningTasks =
            new ConcurrentDictionary<DownloadM3u8TaskItem, bool>();
        public int RunningTaskCount => _runningTasks.Count;
        public int TaskCount => _tasks.Count;
        public int CompletedTaskCount =>
            _tasks.Where(f => f.Status == TaskStatus.Completed).ToList().Count;

        public DownloadM3u8TaskItem AddTask(
            DownloadM3u8TaskItem taskItem,
            Action<CancellationToken, IProgress<int>, DownloadM3u8TaskItem> taskAction
        )
        {
            var existingDownloads = _tasks
                .Where(d => d.RequestUrl.ToString().Equals(taskItem.RequestUrl.ToString()))
                .FirstOrDefault();
            if (existingDownloads is not null)
                return null;
            var progress = new Progress<int>(p => taskItem.Progress = p);

            taskItem.TaskAction = () =>
            {
                taskAction(taskItem.CancellationTokenSource.Token, progress, taskItem);
            };

            _tasks.Add(taskItem);
            string v = JsonConvert.SerializeObject(taskItem, Formatting.None);
            File.AppendAllLines(AppConstants.DownloadM3u8_Batch_File, new string[] { v });
            SetBackgroundColor();
            OnPropertyChanged(nameof(RunningTaskCount));
            OnPropertyChanged(nameof(TaskCount));
            OnPropertyChanged(nameof(CompletedTaskCount));
            StartProcessing();
            return taskItem;
            //StartProcessing();
        }

        public void SetBackgroundColor()
        {
            for (int i = 0; i < _tasks.Count; i++)
            {
                var line = _tasks[i];

                if (line.Status == TaskStatus.Completed)
                {
                    line.BgColour =
                        (i % 2 == 0)
                            ? ListBoxLineBackground.DoneEven
                            : ListBoxLineBackground.DoneOdd;
                }
                else
                {
                    line.BgColour =
                        (i % 2 == 0) ? ListBoxLineBackground.Even : ListBoxLineBackground.Odd;
                }
            }
        }

        public void StartProcessing()
        {
            // 计算可以启动的新任务数量
            int availableSlots = MaxConcurrentTasks - _runningTasks.Count;
            //if(availableSlots < 0)
            //{

            //}
            if (availableSlots > MaxConcurrentTasks)
            {
                availableSlots = MaxConcurrentTasks;
            }
            for (int i = 0; i < availableSlots; i++)
            {
                var nextTask = _tasks.FirstOrDefault(t => t.Status == TaskStatus.Pending);
                if (nextTask == null)
                    break;

                StartTask(nextTask);
            }
        }

        private void StartTask(DownloadM3u8TaskItem task)
        {
            task.Status = TaskStatus.Running;
            _runningTasks[task] = true;
            OnPropertyChanged(nameof(RunningTaskCount));

            try
            {
                Task.Run(async () =>
                {
                    //task.Process();
                    task.TaskAction?.Invoke();
                    _runningTasks.TryRemove(task, out _);
                    OnPropertyChanged(nameof(RunningTaskCount));
                    OnPropertyChanged(nameof(TaskCount));
                    OnPropertyChanged(nameof(CompletedTaskCount));

                    StartProcessing(); // 任务完成后尝试启动新任务
                    if (task.Status == vo.TaskStatus.Running)
                    {
                        task.Speed = "";
                        task.Status = vo.TaskStatus.Completed;
                        task.Progress1 = 1.0;
                    }
                });
            }
            catch (OperationCanceledException)
            {
                task.Status = TaskStatus.Failed;
                task.Progress = 0;
            }
            catch
            {
                task.Status = TaskStatus.Failed;
            }
            finally { }
        }

        public void StartAllTask()
        {
            var failedTasks = _tasks
                .Where(t => t.Status != TaskStatus.Completed && t.Status != TaskStatus.Running)
                .ToList();
            foreach (var task in failedTasks)
            {
                ContinueTask(task);
            }
        }

        public void StopAllTasks()
        {
            foreach (var task in _tasks)
            {
                if (task.Status == TaskStatus.Running || task.Status == TaskStatus.Pending)
                {
                    task.Cancel();
                }
            }
            _runningTasks.Clear();
            OnPropertyChanged(nameof(RunningTaskCount));
            OnPropertyChanged(nameof(TaskCount));
            OnPropertyChanged(nameof(CompletedTaskCount));
        }

        public void ClearCompletedTasks()
        {
            var completedTasks = _tasks.Where(t => t.Status == TaskStatus.Completed).ToList();
            foreach (var task in completedTasks)
            {
                _tasks.Remove(task);
            }
            OnPropertyChanged(nameof(RunningTaskCount));
            OnPropertyChanged(nameof(TaskCount));
            OnPropertyChanged(nameof(CompletedTaskCount));
        }

        public void CancelTask(DownloadM3u8TaskItem task)
        {
            task.Cancel();
            try
            {
                _runningTasks.TryRemove(task, out _);
                OnPropertyChanged(nameof(RunningTaskCount));
                OnPropertyChanged(nameof(TaskCount));
                OnPropertyChanged(nameof(CompletedTaskCount));
                StartProcessing(); // 尝试启动新任务
            }
            catch { }
        }

        public void ContinueTask(DownloadM3u8TaskItem task)
        {
            var progress = new Progress<int>(p => task.Progress = p);
            task.Status = TaskStatus.Pending;
            task.ExitTask = false;
            task.Progress = 0;
            task.Killed = false;
            task.CancellationTokenSource = new CancellationTokenSource();
            task.Progress = 0;
            OnPropertyChanged(nameof(RunningTaskCount));
            OnPropertyChanged(nameof(TaskCount));
            OnPropertyChanged(nameof(CompletedTaskCount));
            StartProcessing();
        }

        private void SaveBatchFile()
        {
            File.Delete(AppConstants.DownloadM3u8_Batch_File);
            var settings = new JsonSerializerSettings
            {
                ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
            };
            foreach (var item in _tasks)
            {
                Dictionary<string, string> dict = new Dictionary<string, string>
                {
                    { "VideoName", item.VideoName },
                    { "RequestUrl", item.RequestUrl.ToString() },
                    { "Method", item.Method.ToString() },
                    { "Iv", item.Iv },
                    { "Key", item.Key },
                };
                string v = JsonConvert.SerializeObject(dict, Formatting.None, settings);
                File.AppendAllLines(AppConstants.DownloadM3u8_Batch_File, new string[] { v });
            }
        }

        public void ClearFailedTasks()
        {
            var failedTasks = _tasks.Where(t => t.Status == TaskStatus.Failed).ToList();
            foreach (var task in failedTasks)
            {
                _tasks.Remove(task);
            }
            SetBackgroundColor();
            SaveBatchFile();
            OnPropertyChanged(nameof(RunningTaskCount));
            OnPropertyChanged(nameof(TaskCount));
            OnPropertyChanged(nameof(CompletedTaskCount));
        }

        public void DeleteSelected(bool deleteCache)
        {
            var list = _tasks.Where(t => SelectedTasks.Contains(t)).ToList();
            foreach (var task in list)
            {
                if(task.Status == TaskStatus.Running || task.Status == TaskStatus.Pending)
                {
                    task.Cancel();
                }
                if (deleteCache)
                {
                    string cachePath = Path.Combine(AppConfig.Instance.SavePath, task.VideoName);
                    if (Directory.Exists(cachePath))
                    {
                        Directory.Delete(cachePath, true);
                    }
                }
                _tasks.Remove(task);
            }
            
            SelectedTasks = new List<DownloadM3u8TaskItem>();
            SetBackgroundColor();

            OnPropertyChanged(nameof(RunningTaskCount));
            OnPropertyChanged(nameof(TaskCount));
            OnPropertyChanged(nameof(CompletedTaskCount));
            SaveBatchFile();
            StartProcessing();
        }

        public void StartSelected()
        {
            var list = _tasks.Where(t => SelectedTasks.Contains(t)).ToList();
            foreach (var task in list)
            {
               
                if (task.Status == TaskStatus.Completed)
                {
                    continue;
                }
                task.Status = TaskStatus.Pending;
            }
            SetBackgroundColor();
            OnPropertyChanged(nameof(RunningTaskCount));
            OnPropertyChanged(nameof(TaskCount));
            OnPropertyChanged(nameof(CompletedTaskCount));
            StartProcessing();
        }

        public void StopSelected()
        {
            var list = _tasks.Where(t => SelectedTasks.Contains(t)).ToList();
            foreach (var task in list)
            {
                task.Cancel();
            }
            SetBackgroundColor();
            OnPropertyChanged(nameof(RunningTaskCount));
            OnPropertyChanged(nameof(TaskCount));
            OnPropertyChanged(nameof(CompletedTaskCount));
            StartProcessing();
        }

        public void OrderByRateTasks()
        {
            var orderedTasks = _tasks.OrderBy(t => t.Progress).ToList();
            _tasks.Clear();
            foreach (var task in orderedTasks)
            {
                _tasks.Remove(task);
                _tasks.Insert(0, task);
            }
            SetBackgroundColor();
        }

        public void OrderByNameTasks()
        {
            var orderedTasks = _tasks.OrderBy(t => t.VideoName).Reverse().ToList();
            _tasks.Clear();
            foreach (var task in orderedTasks)
            {
                _tasks.Remove(task);
                _tasks.Insert(0, task);
            }
            SetBackgroundColor();
        }

        public void ClearAllTask()
        {
            File.Delete(AppConstants.DownloadM3u8_Batch_File);
        }
    }
}
